package com.example.SpringCloudUpload.service;


import com.example.SpringCloudUpload.entity.Chunk;
import com.example.SpringCloudUpload.util.FileCheckUtil;
import com.example.SpringCloudUpload.util.PathUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * Class Description
 *
 * @author hulilei
 * @version 1.0.0
 * @date 2021/10/21 9:19
 */
@Service
public class BigFileService implements BigFileServiceInterface {

    @Autowired
    FileCheckUtil fileCheckUtil;

    @Autowired
    PathUtil pathUtil;

    @Override
    public String fileUploadPost(Chunk chunk, HttpServletResponse response) throws IOException {
        /**
         * 每一个上传块都会包含如下分块信息：
         * chunkNumber: 当前块的次序，第一个块是 1，注意不是从 0 开始的。
         * totalChunks: 文件被分成块的总数。
         * chunkSize: 分块大小，根据 totalSize 和这个值你就可以计算出总共的块数。注意最后一块的大小可能会比这个要大。
         * currentChunkSize: 当前块的大小，实际大小。
         * totalSize: 文件总大小。
         * identifier: 这个就是每个文件的唯一标示。
         * filename: 文件名。
         * relativePath: 文件夹上传的时候文件的相对路径属性。
         * 一个分块可以被上传多次，当然这肯定不是标准行为，但是在实际上传过程中是可能发生这种事情的，这种重传也是本库的特性之一。
         *
         * 根据响应码认为成功或失败的：
         * 200 文件上传完成
         * 201 文加快上传成功
         * 500 第一块上传失败，取消整个文件上传
         * 507 服务器出错自动重试该文件块上传
         */
        // 这里存储的路径是 "C:\test\"
        String filePath = pathUtil.getFileDir(chunk.getUploadType());
        filePath = filePath + "\\" + chunk.getFilename();
        File file = new File(filePath);
        // 第一个块,则新建文件
        if (chunk.getChunkNumber() == 1 && !file.exists()) {
            try {
                file.createNewFile();
            } catch (IOException e) {
                response.setStatus(500);
                return "exception:createFileException";
            }
        }
        // 读取分片的文件输入流
        InputStream fos = chunk.getFile().getInputStream();
        // 如果是第一个分片，需要判断
        if (chunk.getChunkNumber() == 1) {
            /**
             * 文件名后缀进行检查
             */
            if(!fileCheckUtil.checkSuffix(chunk.getFilename(), chunk.getUploadType())) {
                response.setStatus(500);
                file.delete();
                return "exception: upload file suffix not support";
            }
            /**
             * 通过文件头判断文件类型
             * 对文件类型进行检查
             */
            InputStream fos_temp = chunk.getFile().getInputStream();
            boolean check = fileCheckUtil.checkFileType(fos_temp, chunk.getUploadType());
            fos_temp.close();
            if (!check) {
                response.setStatus(500);
                file.delete();
                return "exception: upload file type not support.";
            }
        }
        // 进行写文件操作
        // 将块文件写入文件中
        FileOutputStream outputStream = new FileOutputStream(file, true);
        try {
            if (!chunk.getChunkNumber().equals(chunk.getTotalChunks())) {
                int len = -1;
                /**
                 * 缓冲区大小：
                 * 对于不是最后一个分片，缓冲区大小与分片大小一致
                 * 对于最后一个分片，缓冲区大小设置为两倍分片大小
                 */
                byte[] buffer = new byte[5 * 1024 * 1024];
                while ((len = fos.read(buffer)) != -1) {
                    /**
                     * 将分片文件流写入文件
                     */
                    outputStream.write(buffer, 0, len);
                }
            } else {
                int len = -1;
                byte[] buffer = new byte[10 * 1024 * 1024];
                while ((len = fos.read(buffer)) != -1) {
                    /**
                     * 将分片文件流写入文件
                     */
                    outputStream.write(buffer, 0, len);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
            if (chunk.getChunkNumber() == 1) {
                file.delete();
            }
            response.setStatus(507);
            return "exception: writeFileException";
        } finally {
            if (fos != null) {
                fos.close();
            }
            outputStream.close();
        }
        /**
         * 接收的分片数等于总的分片数，说明文件上传完成
         * 反之，说明此时文件还未上传完成
         */
        if (chunk.getChunkNumber().equals(chunk.getTotalChunks())) {
            response.setStatus(200);
            // TODO 向数据库中保存上传信息
            /**
             * 返回文件在服务器上的路径
             */
            return "upload file successed";
        } else {
            response.setStatus(201);
            // 第一个分片，返回文件路径
            if(chunk.getChunkNumber() == 1) {
                return filePath;
            }
            else {
                return "ok";
            }
        }
    }

    @Override
    public String removeFile(String filePath) {
        File file = new File(filePath);
        try {
            file.delete();
            return "delete " + filePath + " successed!";
        } catch (Exception e) {
            return "delete " + filePath + " failed!";
        }
    }
}
